package com.cxj.cxj_test_cloud.common.rxjava;

import com.trello.rxlifecycle3.LifecycleProvider;
import com.trello.rxlifecycle3.android.ActivityEvent;
import com.trello.rxlifecycle3.android.FragmentEvent;
import com.trello.rxlifecycle3.components.support.RxFragment;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.subjects.PublishSubject;
import io.reactivex.subjects.Subject;

public class RxBus {
    //todo volatile确保多个线程在获取单例实例时，不会得到未初始化的对象
    private volatile static RxBus mDefaultInstance;
    //todo Subject`既是观察者（可以订阅其他Observable）又是被观察者（可以被其他Observer订阅）。这里我们使用`Subject`来作为事件总线的中枢，所有的事件都通过这个`Subject`来发送和接收
    private final Subject<Object> mBus;
    private final Map<Class<?>, Object> mStickyEventMap;//存放 Sticky 事件

    private RxBus() {
        /**
         * 当我们发送一个粘性事件时，这个事件会被存入这个Map中，同时也会通过`mBus`发送（这样当前的订阅者就能收到）。
         * 当有新的订阅者订阅该类型的事件时，我们会先从Map中取出该类型的事件（如果有的话）发送给订阅者，然后再订阅`mBus`的后续事件
         */
        //todo 创建一个 线程安全 的 PublishSubject(把 RxBus 做成一个 全局事件总线：任何线程都能随时 post() 事件，任何线程也都能随时 subscribe())
        mBus = PublishSubject.create().toSerialized();//普通事件
        //todo 线程安全的 Map，存放 Sticky 事件，即“发送后仍保留，后续订阅者还能收到”的事件。
        mStickyEventMap = new ConcurrentHashMap<>();//粘性事件  todo 怎么操作执行的
    }

    public static RxBus getInstance() {
        if (mDefaultInstance == null) {
            synchronized (RxBus.class) {
                if (mDefaultInstance == null) {
                    mDefaultInstance = new RxBus();

                }
            }
        }
        return mDefaultInstance;
    }

    /**
     * 发送一个新Sticky事件
     */
    public void postSticky(Object event) {
        synchronized (mStickyEventMap) { //todo 线性安全
            mStickyEventMap.put(event.getClass(), event);
        }
        post(event);//普通事件
    }

    /**
     * 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者   todo 订阅粘性事件
     * LifecycleProvider：RxLifecycle 的接口，能感知 Activity/Fragment 生命周期，防止内存泄漏,RxJava 的流在生命周期安全的时候自动停止，避免内存泄漏和 IllegalStateException
     */
    //todo 返回 Observable<T> 的目的，是让调用者拿到一条“随时可订阅、随时可组合、随时可取消”的响应式事件流，从而把『等待事件』变成『声明式处理事件』
    public <T> Observable<T> toObservableSticky(final Class<T> eventType, LifecycleProvider lifecycleProvider) {
        synchronized (mStickyEventMap) {//todo 加锁
            Object destory = ActivityEvent.DESTROY;//默认
            //todo  检查缓存中是否有历史事件
            final Object event = mStickyEventMap.get(eventType);
            Observable observable;
            if (lifecycleProvider != null) {
                if (lifecycleProvider instanceof com.trello.rxlifecycle3.components.support.RxFragment) {//彻底避免内存泄漏
                    destory = FragmentEvent.DESTROY;
                }
                //todo  ofType:从总线 mBus 里过滤并只发射类型为 eventType 的事件   利用 compose(bindUntilEvent) 自动跟生命周期绑定。
                observable = mBus.ofType(eventType).compose(lifecycleProvider.bindUntilEvent(destory));
            } else {
                observable = mBus.ofType(eventType);
            }

            if (event != null) {
                //todo mergeWith把两条流合并成一条   一条是“实时事件流”，一条是“一次性缓存事件”
                //todo Observable.create构造一条 只发射一次 的流   让缓存值像“普通事件”一样发射出去
                return observable.mergeWith(Observable.create(new ObservableOnSubscribe<T>() {
                    @Override
                    public void subscribe(ObservableEmitter<T> subscriber) throws Exception {
                        //todo 把缓存值强转后发射    保证类型安全，避免 ClassCastException
                        subscriber.onNext(eventType.cast(event));
                    }
                }));
            } else {
                return observable;
            }
        }
    }

    /**
     * 根据eventType获取Sticky事件
     */
    //todo 返回值 T：正是这个 Class 所代表的类型——强类型安全，不需要调用方再做强制转型
    public <T> T getStickyEvent(Class<T> eventType) {
        synchronized (mStickyEventMap) {
            //todo 利用 Class#cast 做 运行时类型转换，把 Object 转成 T
            return eventType.cast(mStickyEventMap.get(eventType));
        }
    }

    /**
     * 移除指定eventType的Sticky事件
     */
    //todo 返回值 T：正是这个 Class 所代表的类型——强类型安全，不需要调用方再做强制转型
    public <T> T removeStickyEvent(Class<T> eventType) {
        synchronized (mStickyEventMap) {
            //todo 利用 Class#cast 做 运行时类型转换，把 Object 转成 T
            return eventType.cast(mStickyEventMap.remove(eventType));
        }
    }

    /**
     * 移除所有的Sticky事件
     */
    public void removeAllStickyEvents() {
        synchronized (mStickyEventMap) {
            mStickyEventMap.clear();
        }
    }

    /**
     * 发送事件
     */
    public void post(Object event) {
        mBus.onNext(event);//todo 内部怎么执行的（立即遍历当前所有 已订阅 的 Observer/Subscriber,同步或异步（取决于订阅者的调度器）把事件推给每个监听者。)
    }

    /**
     * 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者
     * 订阅普通事件
     */
    public <T> Observable<T> toObservable(final Class<T> eventType, LifecycleProvider lifecycleProvider) {
        Object destory = ActivityEvent.DESTROY;
        if (lifecycleProvider != null) {
            if (lifecycleProvider instanceof RxFragment) {
                destory = FragmentEvent.DESTROY;
            }
            return mBus
                    .ofType(eventType)
                    .compose(lifecycleProvider.bindUntilEvent(destory));
        } else {
            return mBus
                    .ofType(eventType);
        }

    }

    /**
     * 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者
     */
    public <T> Observable<T> toObservable(final Class<T> eventType) {
        return mBus.ofType(eventType);

    }

    /**
     * 判断是否有订阅者
     */
    public boolean hasObservers() {
        return mBus.hasObservers();
    }

    /**
     * 清空
     */
    public void reset() {
        mDefaultInstance = null;
    }

}
